﻿using CaseManagerLibrary.Http;
using CaseManagerLibrary.Licence;
using CaseManagerLibrary.Models;
using CaseManagerLibrary.Socket;
using IMAPI2.Interop;
using Newtonsoft.Json;
using PSDClientService.Burn;
using PSDClientService.SystemSettings;
using PSDLib;
using PSDLib.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PSDClientService
{
    public delegate void SetDeviceInfoEventHandler(dl_download_device device);
    public delegate void GetRecordingDeviceEvnetHandler(RecorderDevice recorder);

    public class DeviceManager
    {
        public event GetRecordingDeviceEvnetHandler OnGetRecordingDevice;
        public event UpdateCapacityEventHandler OnUpdateCapacity;
        public event BurnProgressEventHandler OnBurnProgress;
        public event BurnSuccessEventhandler OnBurnSuccess;
        public event BurnErrorEventHandler OnBurnError;
        public event BurnCannelEventHanlder OnBurnCancel;
        public event DetectRecorderEventHandler OnDetectRecorder;

        private ClientServiceContext context;

        public event SetDeviceInfoEventHandler OnSetDeviceInfo;
        private Semaphore DeviceSemaphore { get; set; }
        public bool BurnEnable { get; set; }
        public List<RecorderDevice> RecorderList { get; set; }

        private System.Timers.Timer rebootTimer;

        public DeviceManager(ClientServiceContext ctx)
        {
            //LocalStorageList = new List<StorageInfo>();
            context = ctx;

            RecorderList = new List<RecorderDevice>();

            rebootTimer = new System.Timers.Timer();
            rebootTimer.Enabled = false;
            rebootTimer.Interval = 1000 * 60;
            rebootTimer.Elapsed += RebootTimer_Elapsed;
        }

        private void RebootTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (AppSettings.Setting.DeviceRebootTime != null && AppSettings.Setting.DeviceRebootTime.Length > 0)
            {
                DateTime now = DateTime.Now;

                if (AppSettings.Setting.DeviceRebootTime.Contains(now.ToString("HH:mm")))
                {
                    RebootDevice(DeviceRebootType.常规重启);
                }
            }
        }

        public void RebootDevice(DeviceRebootType rebootType)
        {
            string path = System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "PSD", "Log", rebootType.ToString() + ".log");
            var sw = System.IO.File.AppendText(path);
            string log = $@"{System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {rebootType.ToString()}";
            sw.WriteLine(log);
            sw.Close();

            context.RebootDevice();
        }

        public bool Init()
        {
            bool success = true;

            DeviceSemaphore = new Semaphore(AppSettings.Setting.RecorderCount, AppSettings.Setting.RecorderCount);

            //注册当前设备
            //context.UUID = DeviceUUID.GetUUID();
            context.Logger.Info("获取当前设备ID：" + context.UUID);

            this.context.Device = GetDeviceRegistInfo(context.UUID);

            if (this.context.Device != null)
            {
                LicenceCheckCode code = CheckLicence();

                if (code == LicenceCheckCode.Success)
                {
                    rebootTimer.Enabled = true;

                    success = true;
                }
                else
                {
                    success = false;
                    context.Logger.Error("设备授权信息验证失败：" + code.ToString());
                }
            }
            else
            {
                success = false;
            }

            return success;
        }

        public LicenceCheckCode CheckLicence()
        {
            LicenceCheckCode code = LicenceCheckCode.Unknown;

            var files = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.pal");

            if (files.Length > 0)
            {
                var licence = LicenceManager.GetLicenceContentFromFile(files[0], context.Device.id);

                code = LicenceManager.Check(licence, context.Device.id, LicenceDaysFrom.SysTime);
            }
            else
            {
                code = LicenceCheckCode.NoLicenceFile;
            }

            return code;
        }

        /// <summary>
        /// 查询指定设备的注册信息，若不存在则进行注册123
        /// </summary>
        /// <param name="uuid"></param>
        /// <returns></returns>
        public dl_download_device GetDeviceRegistInfo(string uuid)
        {
            dl_download_device device = null;

            string licenceFile = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "licence.pal");
            System.IO.File.Delete(licenceFile);

            //LocalStorageList = PSDFunction.GetLocalStorage(null);

            var result = HttpHelper.Get<HResult<dl_download_device>>(AppSettings.Setting.API.GetDownloadDeviceInfo + "?deviceId=" + uuid);

            bool storageChanged = false;

            if (result.success && result.data != null)
            {
                device = result.data;
                device.device_ip = NetworkHelper.GetLocalIP();
                device.device_port = AppSettings.Setting.PSDListenPort;
                device.burn_enable = (GetRecordingDevice() ? 1 : 0);
                device.burn_port = AppSettings.Setting.BurnListenPort;
                device.recorder_count = RecorderList.Count;
                device.RecorderList = RecorderList.Select(t => t.Name).ToList();

                storageChanged = PSDFunction.GetLocalStorage(device);

                if (!String.IsNullOrEmpty(device.device_key))
                {
                    System.IO.File.WriteAllText(licenceFile, device.device_key);
                }

                AppSettings.Setting.PSDListenPort = device.device_port;
                AppSettings.Setting.LogPath = device.log_path;
                AppSettings.Setting.DownloadDataPath = device.data_path;
                AppSettings.Setting.MaxDownloadProcess = device.max_download_process;
                AppSettings.Setting.Save();

                if (context.Logger != null)
                {
                    context.Logger.Info("更新设备注册信息：ip=" + device.device_ip + ":" + device.device_port + " current storage=" + device.current_storage);
                }
            }
            else
            {
                device = new dl_download_device();
                device.id = uuid;
                device.device_ip = NetworkHelper.GetLocalIP();
                device.device_port = AppSettings.Setting.PSDListenPort;
                device.device_name = System.Environment.MachineName;
                device.device_connect_type = AppSettings.Setting.DeviceConnectType;
                device.data_path = AppSettings.Setting.DownloadDataPath;
                device.log_path = AppSettings.Setting.LogPath;
                //device.dl_server_id = (context.DownloadManager.DownloadServerList.Count > 0 ? context.DownloadManager.DownloadServerList[0].id : "");
                device.max_download_process = AppSettings.Setting.MaxDownloadProcess;
                device.storage_available_percent = AppSettings.Setting.StorageAvailablePercent;
                device.state = 0;
                device.device_version = AppSettings.Setting.DeviceVersion;
                device.device_pwd = AppSettings.Setting.DefaultPassword;
                device.device_pwd_salt = AppSettings.Setting.DefaultSalt;
                device.storage_type = 0;
                device.burn_enable = (GetRecordingDevice() ? 1 : 0);
                device.burn_port = AppSettings.Setting.BurnListenPort;
                device.recorder_count = RecorderList.Count;
                device.RecorderList = RecorderList.Select(t => t.Name).ToList();

                storageChanged = PSDFunction.GetLocalStorage(device);

                ApplyStorageDefaultRule(device);

                //if (device.StorageList.Count > 0)
                //{
                //    var ms = LocalStorageList[0];
                //    device.main_storage = ms.DiskDriver;
                //    device.main_storage_total = ms.SumSpace;
                //    device.main_storage_free = ms.FreeSpace;

                //    device.current_storage = device.main_storage;
                //}

                //if (LocalStorageList.Count > 1)
                //{
                //    var ss = LocalStorageList[1];
                //    device.second_storage = ss.DiskDriver;
                //    device.second_storage_total = ss.SumSpace;
                //    device.second_storage_free = ss.FreeSpace;
                //}

                if (context.Logger != null)
                {
                    context.Logger.Info("注册新的PSD设备：ip=" + device.device_ip + ":" + device.device_port + "  current storage=" + device.current_storage);
                }
            }

            var res = RegistDevice(device);

            if (res.success)
            {
                if (!storageChanged)
                {
                    return device;
                }
                else
                {
                    context.Logger.Error("设备存储已发生改变，请重新配置");
                    return null;
                }
            }
            else
            {
                //MessageBox.Show("当前下载设备注册失败");
                return null;
            }

        }


        private void ApplyStorageDefaultRule(dl_download_device device)
        {
            if (device.StorageList.Count == 4)
            {
                device.StorageList[0].storage_type = 0;
                device.StorageList[0].storage_group = 1;
                device.StorageList[0].state = 1;

                device.StorageList[1].storage_type = 0;
                device.StorageList[1].storage_group = 2;
                device.StorageList[1].state = 0;

                device.StorageList[2].storage_type = 1;
                device.StorageList[2].storage_group = 1;
                device.StorageList[2].state = 0;

                device.StorageList[3].storage_type = 1;
                device.StorageList[3].storage_group = 2;
                device.StorageList[3].state = 0;
            }

            if (device.StorageList.Count == 2)
            {
                device.StorageList[0].storage_type = 0;
                device.StorageList[0].storage_group = 1;
                device.StorageList[0].state = 1;

                device.StorageList[1].storage_type = 1;
                device.StorageList[1].storage_group = 1;
                device.StorageList[1].state = 0;
            }

            if (string.IsNullOrEmpty(device.current_storage) && device.StorageList.Count > 0)
            {
                device.current_storage = device.StorageList[0].storage_driver;
            }
        }
        /// <summary>
        /// 注册设备
        /// </summary>
        /// <param name="device"></param>
        /// <returns></returns>
        public HResult RegistDevice(dl_download_device device)
        {
            var result = HttpHelper.Post<HResult>(AppSettings.Setting.API.RegistDownloadDevice, JsonConvert.SerializeObject(device));

            if (context.Logger != null)
            {
                if (result.success)
                {
                    context.Logger.Log("更新PSD注册信息成功");
                }
                else
                {
                    context.Logger.Error("更新PSD注册信息失败");
                }
            }

            return result;
        }

        #region 自动格式化下载设备硬盘

        /// <summary>
        /// 初始化存储
        /// </summary>
        public void InitStorage()
        { }

        /// <summary>
        /// 格式化硬盘
        /// </summary>
        public void FormatHardDisk()
        { }
        #endregion

        #region 下载存储设备
        public void UpdateLocalStorage()
        {
            PSDFunction.UpdateLocalStorage(context.Device);
        }

        public bool CheckCurrentStorage()
        {
            bool result = false;

            PSDFunction.UpdateLocalStorage(context.Device);

            var disk = context.Device.StorageList.Where(t => t.state == 1).FirstOrDefault();

            if (disk == null)
            {
                context.Logger.Error("无可用存储");
                return false;
            }

            if ((disk.storage_free_size / disk.storage_total_size) > (AppSettings.Setting.StorageAvailablePercent / 100))
            {
                result = true;
            }
            else
            {
                double max_free = context.Device.StorageList.Where(t => t.state == 0 && t.storage_type == 0).Max(m => m.storage_free_size);

                var max_free_disk = context.Device.StorageList.Where(t => t.state == 0 && t.storage_type == 0 && t.storage_free_size == max_free).FirstOrDefault();

                if (max_free_disk != null)
                {
                    if ((max_free_disk.storage_free_size / max_free_disk.storage_total_size) > (AppSettings.Setting.StorageAvailablePercent / 100))
                    {
                        var old_st = context.Device.StorageList.Where(t => t.storage_driver == context.Device.current_storage).FirstOrDefault();
                        if (old_st != null)
                        {
                            old_st.state = 0;
                        }

                        context.Device.current_storage = max_free_disk.storage_driver;
                        var new_st = context.Device.StorageList.Where(t => t.storage_driver == context.Device.current_storage).FirstOrDefault();
                        if (new_st != null)
                        {
                            new_st.state = 1;
                        }

                        Dictionary<string, string> data = new Dictionary<string, string>();
                        data.Add("deviceId", context.UUID);
                        data.Add("oldStorage", disk.storage_driver);
                        data.Add("currStorage", context.Device.current_storage);

                        var ret = HttpHelper.Post<HResult>(AppSettings.Setting.API.SetCurrentDownloadStorage, data);

                        if (ret.success)
                        {
                            context.Logger.Log("当前存储盘已切换至" + context.Device.current_storage);
                        }
                        result = ret.success;
                    }
                    else
                    {
                        context.Logger.Error("剩余可用存储过小");
                        result = false;
                    }
                }
                else
                {
                    context.Logger.Error("无剩余可用存储");
                    result = false;
                }
            }

            lock (context.DownloadContext.lock_Downloading)
            {
                context.DownloadContext.Downloading = result;
            }

            //if (!result)
            //{
            //    context.Logger.Error("可用存储空间不足");
            //}

            return result;
        }

        public void SetPSDName(string deviceName)
        {
            context.Device.device_name = deviceName;

            OnSetDeviceInfo?.Invoke(context.Device);
        }

        public void SetPSDMaxDownloadProcess(int process)
        {
            context.Device.max_download_process = process;
            AppSettings.Setting.MaxDownloadProcess = process;
            AppSettings.Setting.Save();
        }

        #endregion

        #region 刻录设备
        private bool GetRecordingDevice()
        {
            //
            // Determine the current recording devices
            //
            MsftDiscMaster2 discMaster = null;
            try
            {
                context.Logger.Info("Init Recorder Device");

                discMaster = new MsftDiscMaster2();

                if (!discMaster.IsSupportedEnvironment)
                {
                    context.Logger.Error("设备不支持刻录功能");

                    this.BurnEnable = false;
                    return this.BurnEnable;
                }

                for (int i = discMaster.Count - 1; i >= 0; i--)
                {
                    var recorder = new RecorderDevice(context.BurnContext, discMaster[i]);
                    recorder.OnBurnProgress += Recoder_OnBurnProgress;
                    recorder.OnBurnSuccess += Recoder_OnBurnSuccess;
                    recorder.OnBurnError += Recoder_OnBurnError;
                    recorder.OnBurnCancel += Recorder_OnBurnCancel;
                    recorder.OnDetectRecorder += Recorder_OnDetectRecorder;
                    recorder.OnUpdateCapacity += Recorder_OnUpdateCapacity;
                    RecorderList.Insert(0, recorder);

                    OnGetRecordingDevice?.Invoke(recorder);

                    context.Logger.Log($"{RecorderList.Count}.{recorder.RecorderId} - {recorder.VolumePathName}[{recorder.ProductId}]");
                }
                //foreach (string uniqueRecorderId in discMaster)
                //{
                //    var recorder = new RecorderDevice(context.BurnContext, uniqueRecorderId);
                //    recorder.OnBurnProgress += Recoder_OnBurnProgress;
                //    recorder.OnBurnSuccess += Recoder_OnBurnSuccess;
                //    recorder.OnBurnError += Recoder_OnBurnError;
                //    recorder.OnBurnCancel += Recorder_OnBurnCancel;
                //    recorder.OnDetectRecorder += Recorder_OnDetectRecorder;
                //    recorder.OnUpdateCapacity += Recorder_OnUpdateCapacity;
                //    RecorderList.Insert(0, recorder);

                //    OnGetRecordingDevice?.Invoke(recorder);

                //    context.Logger.Log($"{RecorderList.Count}.{recorder.RecorderId} - {recorder.VolumePathName}[{recorder.ProductId}]");
                //}

                this.BurnEnable = true;
            }
            catch (COMException ex)
            {
                context.Logger.Error(string.Format("BurnManager Init Error:{0} - Please install IMAPI2", ex.ErrorCode));
                this.BurnEnable = false;
            }
            finally
            {
                if (discMaster != null)
                {
                    Marshal.ReleaseComObject(discMaster);
                }
            }

            //success = UpdateCapacity();

            return this.BurnEnable;
        }

        private void Recorder_OnBurnCancel(RecorderDevice device, BurnJob burnJob, string info)
        {
            OnBurnCancel?.Invoke(device, burnJob, info);
        }

        private void Recorder_OnUpdateCapacity(RecorderDevice device, int resultCode, long totalDiscSize, long totalMediaSize)
        {
            OnUpdateCapacity?.Invoke(device, resultCode, totalDiscSize, totalMediaSize);
        }

        private void Recorder_OnDetectRecorder(RecorderDevice device, int resultCode, string info)
        {
            OnDetectRecorder?.Invoke(device, resultCode, info);
        }

        private void Recoder_OnBurnError(RecorderDevice device, BurnJob burnJob, string info)
        {
            OnBurnError?.Invoke(device, burnJob, info);

            //ReleaseDevice();
        }

        private void Recoder_OnBurnSuccess(RecorderDevice device, BurnJob burnJob, string info)
        {
            OnBurnSuccess?.Invoke(device, burnJob, info);

            //ReleaseDevice();
        }

        private void Recoder_OnBurnProgress(RecorderDevice device, BurnJob burnJob, string info, int percent)
        {
            OnBurnProgress?.Invoke(device, burnJob, info, percent);
        }

        private object lock_recorder = new object();
        /// <summary>
        /// 获取当前可用的刻录设备
        /// </summary>
        /// <returns></returns>
        public int GetFreeDevice(out RecorderDevice device)
        {
            int result = (int)ResultCode.Success;

            device = null;

            //if (WaitDevice())
            //{
            lock (lock_recorder)
            {
                device = RecorderList.Where(t => t.State == RecorderState.Free).FirstOrDefault();

                if (device != null)
                {
                    result = SetCurrentRecorder(device);
                    if (result == (int)ResultCode.Success)
                    {
                        device.State = RecorderState.Burning;
                    }
                }
                else
                {
                    result = (int)ResultCode.NoFreeRecorder;
                }
            }
            //}

            return result;
        }

        /// <summary>
        /// 等待直到获得一个可用设备信号量
        /// </summary>
        public bool WaitDevice()
        {
            return DeviceSemaphore.WaitOne();
        }

        /// <summary>
        /// 释放一个可用设备信号量
        /// </summary>
        public void ReleaseDevice(string id)
        {
            //DeviceSemaphore.Release();

            lock (lock_recorder)
            {
                var recorder = RecorderList.Where(t => t.RecorderId == id).FirstOrDefault();

                recorder.State = RecorderState.Free;
            }

        }

        /// <summary>
        /// 设置要使用的刻录设备
        /// </summary>
        /// <param name="discRecorder"></param>
        public int SetCurrentRecorder(RecorderDevice device)
        {
            //
            // Verify recorder is supported
            //

            int result = (int)ResultCode.Success;
            IDiscFormat2Data discFormatData = null;
            try
            {
                discFormatData = new MsftDiscFormat2Data();
                if (!discFormatData.IsRecorderSupported(device.Recorder))
                {
                    //MessageBox.Show("不被支持的刻录设备", "提示",
                    //    MessageBoxButtons.OK, MessageBoxIcon.Error);
                    result = (int)ResultCode.NoFreeRecorder;
                    OnDetectRecorder?.Invoke(device, result, "当前设备不支持刻录");
                    return result;
                }

                StringBuilder supportedMediaTypes = new StringBuilder();
                foreach (IMAPI_PROFILE_TYPE profileType in device.Recorder.SupportedProfiles)
                {
                    string profileName = GetProfileTypeString(profileType);

                    if (string.IsNullOrEmpty(profileName))
                        continue;

                    if (supportedMediaTypes.Length > 0)
                        supportedMediaTypes.Append(", ");
                    supportedMediaTypes.Append(profileName);
                }

                //result = device.DetectRecorder();
                //supportedMediaLabel.Text = supportedMediaTypes.ToString();
            }
            catch (COMException)
            {
                result = (int)ResultCode.Failed;
                //supportedMediaLabel.Text = "Error getting supported types";
            }
            finally
            {
                if (discFormatData != null)
                {
                    Marshal.ReleaseComObject(discFormatData);
                }
            }

            return result;
        }

        static string GetProfileTypeString(IMAPI_PROFILE_TYPE profileType)
        {
            switch (profileType)
            {
                default:
                    return string.Empty;

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_CD_RECORDABLE:
                    return "CD-R";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_CD_REWRITABLE:
                    return "CD-RW";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVDROM:
                    return "DVD ROM";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_DASH_RECORDABLE:
                    return "DVD-R";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_RAM:
                    return "DVD-RAM";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_PLUS_R:
                    return "DVD+R";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_PLUS_RW:
                    return "DVD+RW";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_PLUS_R_DUAL:
                    return "DVD+R Dual Layer";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_DASH_REWRITABLE:
                    return "DVD-RW";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_DASH_RW_SEQUENTIAL:
                    return "DVD-RW Sequential";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_DASH_R_DUAL_SEQUENTIAL:
                    return "DVD-R DL Sequential";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_DASH_R_DUAL_LAYER_JUMP:
                    return "DVD-R Dual Layer";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_DVD_PLUS_RW_DUAL:
                    return "DVD+RW DL";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_HD_DVD_ROM:
                    return "HD DVD-ROM";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_HD_DVD_RECORDABLE:
                    return "HD DVD-R";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_HD_DVD_RAM:
                    return "HD DVD-RAM";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_BD_ROM:
                    return "Blu-ray DVD (BD-ROM)";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_BD_R_SEQUENTIAL:
                    return "Blu-ray media Sequential";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_BD_R_RANDOM_RECORDING:
                    return "Blu-ray media";

                case IMAPI_PROFILE_TYPE.IMAPI_PROFILE_TYPE_BD_REWRITABLE:
                    return "Blu-ray Rewritable media";
            }
        }
        #endregion
    }
}
